Namespace.Use("UI.Object", function(ns) {
    //
    //  Imports.
    //
    var Inherit = Namespace.Import("Base.Inherit");

    //
    //  Internal classes.
    //
    var HorizontalAlignment = Polymorphic.Create(
        [String],
        function(value) {
            this.AccessValue = Polymorphic.Create(
                [],
                function() {
                    return value;
                }
            );
        }
    );
    var VerticalAlignment = Polymorphic.Create(
        [String],
        function(value) {
            this.AccessValue = Polymorphic.Create(
                [],
                function() {
                    return value;
                }
            );
        }
    );

    //  Horizontal alignments.
    var HR_ALIGN_LEFT = new HorizontalAlignment("left");
    var HR_ALIGN_CENTER = new HorizontalAlignment("center");
    var HR_ALIGN_RIGHT = new HorizontalAlignment("right");

    //  Vertical alignments.
    var VT_ALIGN_TOP = new VerticalAlignment("top");
    var VT_ALIGN_MIDDLE = new VerticalAlignment("middle");
    var VT_ALIGN_BOTTOM = new VerticalAlignment("bottom");

    //  Global.
    var cst_InitialHorizontalAlignment = HR_ALIGN_LEFT;
    var cst_InitialVerticalAlignment = VT_ALIGN_MIDDLE;
    var cst_InitialX = 0, cst_InitialY = 0;
    var cst_InitialWidth = 100, cst_InitialHeight = 100;
    var cst_InitialVisibility = true;
    var cst_InitialLayer = 0;
    var cst_InitialFont = "normal 9pt Arial";
    var cst_InitialForegroundColor = "black";
    var cst_InitialBackgroundColor = "transparent";
    var cst_PixelRatio = (function () {
        var ctx = document.createElement("canvas").getContext("2d"),
            dpr = window.devicePixelRatio || 1,
            bsr = ctx.webkitBackingStorePixelRatio ||
                  ctx.mozBackingStorePixelRatio ||
                  ctx.msBackingStorePixelRatio ||
                  ctx.oBackingStorePixelRatio ||
                  ctx.backingStorePixelRatio || 1;
        return dpr / bsr;
    })();

    //
    //  Public classes.
    //
    var Label = Polymorphic.Create(
        [String, String],
        function(text, font) {
            //  Let parent initialize.
            ns.BaseObject.call(this);

            //
            //  Members.
            //
            var last_x = -1, last_y = -1;
            var x = cst_InitialX, y = cst_InitialY;
            var last_width = -1, last_height = -1;
            var width = cst_InitialWidth, height = cst_InitialHeight;
            var last_visibility = !cst_InitialVisibility;
            var visibility = cst_InitialVisibility;
            var last_layer = -1;
            var layer = cst_InitialLayer;
            var last_font = null;
            var last_text = null;
            var last_hr_align = null, last_vt_align = null;
            var hr_align = cst_InitialHorizontalAlignment, vt_align = cst_InitialVerticalAlignment;
            var last_frcolor = null, last_bgcolor = null;
            var frcolor = cst_InitialForegroundColor, bgcolor = cst_InitialBackgroundColor;
            var element = $("<canvas>").css({
                "position": "absolute",
                "background-color": "transparent"
            });
            var context = element[0].getContext("2d");

            //  Set the graphic transformation parameters.
            context.setTransform(cst_PixelRatio, 0, 0, cst_PixelRatio, 0, 0);

            //
            //  Private methods.
            //
            function GetEventOffsetPosition(event) {
                var offset = element.offset();
                return [
                    event.offsetX || event.pageX - offset.left,
                    event.offsetY || event.pageY - offset.top
                ];
            }

            //
            //  Public methods.
            //
            this.GetHTMLElement = function() {
                return element[0];
            };
            this.Invalidate = function() {
                var refresh = false;

                //  Update the size.
                if (last_x != x) {
                    element.css({
                        "left": x
                    });
                    last_x = x;
                    refresh = true;
                }
                if (last_y != y) {
                    element.css({
                        "top": y
                    });
                    last_y = y;
                    refresh = true;
                }

                //  Update the size.
                if (last_width != width) {
                    element.css({
                        "width": width
                    });
                    element[0].width = width * cst_PixelRatio;
                    last_width = width;
                    refresh = true;
                }
                if (last_height != height) {
                    element.css({
                        "height": height
                    });
                    element[0].height = height * cst_PixelRatio;
                    last_height = height;
                    refresh = true;
                }

                //  Update the visibility.
                if (last_visibility != visibility) {
                    if (visibility == true) {
                        element.show();
                    } else {
                        element.hide();
                    }
                    last_visibility = visibility;
                    refresh = true;
                }

                //  Update the layer.
                if (last_layer != layer) {
                    element.css({
                        "z-index": layer
                    });
                    last_layer = layer;
                    refresh = true;
                }

                //  Update the alignment.
                if (last_hr_align != hr_align) {
                    last_hr_align = hr_align;
                    refresh = true;
                }
                if (last_vt_align != vt_align) {
                    last_vt_align = vt_align;
                    refresh = true;
                }

                //  Update the colors.
                if (last_frcolor != frcolor) {
                    last_frcolor = frcolor;
                    refresh = true;
                }
                if (last_bgcolor != bgcolor) {
                    element.css({
                        "background-color": bgcolor
                    });
                    last_bgcolor = bgcolor;
                    refresh = true;
                }

                //  Update the text.
                if (last_text != text) {
                    last_text = text;
                    refresh = true;
                }

                //  Refresh.
                if (refresh == true) {
                    //  Clear the canvas.
                    context.clearRect(0, 0, width, height);

                    //  Set the alignment.
                    context.textAlign = hr_align.AccessValue();
                    context.textBaseline = vt_align.AccessValue();

                    //  Set the font.
                    context.font = font;

                    //  Calculate the drawing position.
                    var dw_x, dw_y;
                    if (hr_align == HR_ALIGN_LEFT) {
                        dw_x = 0;
                    } else if (hr_align == HR_ALIGN_RIGHT) {
                        dw_x = width;
                    } else {
                        dw_x = width / 2;
                    }
                    if (vt_align == VT_ALIGN_TOP) {
                        dw_y = 0;
                    } else if (vt_align == VT_ALIGN_BOTTOM) {
                        dw_y = height;
                    } else {
                        dw_y = height / 2;
                    }

                    //  Set the text color.
                    context.fillStyle = frcolor;

                    //  Draw the text.
                    context.fillText(text, dw_x, dw_y);
                }
            };
            this.AccessPosition = Polymorphic.Create(
                [],
                function() {
                    return [x, y];
                },
                [Number, Number],
                function(new_x, new_y) {
                    last_x = x;
                    last_y = y;
                    x = new_x;
                    y = new_y;
                    this.Invalidate();
                }
            );
            this.AccessSize = Polymorphic.Create(
                [],
                function() {
                    return [width, height];
                },
                [Number, Number],
                function(new_width, new_height) {
                    last_width = width;
                    last_height = height;
                    width = new_width;
                    height = new_height;
                    this.Invalidate();
                }
            );
            this.AccessVisibility = Polymorphic.Create(
                [],
                function() {
                    return visibility;
                },
                [Boolean],
                function(new_visibility) {
                    last_visibility = visibility;
                    visibility = new_visibility;
                    this.Invalidate();
                }
            );
            this.AccessLayer = Polymorphic.Create(
                [],
                function() {
                    return layer;
                },
                [Number],
                function(new_layer) {
                    last_layer = layer;
                    layer = new_layer;
                    this.Invalidate();
                }
            );
            this.AccessText = Polymorphic.Create(
                [],
                function() {
                    return text;
                },
                [String],
                function(new_text) {
                    last_text = text;
                    text = new_text;
                    this.Invalidate();
                }
            );
            this.AccessFont = Polymorphic.Create(
                [],
                function() {
                    return font;
                },
                [String],
                function(new_font) {
                    last_font = font;
                    font = new_font;
                    this.Invalidate();
                }
            );
            this.AccessBackgroundColor = Polymorphic.Create(
                [],
                function() {
                    return bgcolor;
                },
                [String],
                function(new_bgcolor) {
                    last_bgcolor = bgcolor;
                    bgcolor = new_bgcolor;
                    this.Invalidate();
                }
            );
            this.AccessForegroundColor = Polymorphic.Create(
                [],
                function() {
                    return frcolor;
                },
                [String],
                function(new_frcolor) {
                    last_frcolor = frcolor;
                    frcolor = new_frcolor;
                    this.Invalidate();
                }
            );
            this.AccessHorizontalAlignment = Polymorphic.Create(
                [],
                function() {
                    return hr_align;
                },
                [HorizontalAlignment],
                function(new_alignment) {
                    last_hr_align = hr_align;
                    hr_align = new_alignment;
                    this.Invalidate();
                }
            );
            this.AccessVerticalAlignment = Polymorphic.Create(
                [],
                function() {
                    return vt_align;
                },
                [VerticalAlignment],
                function(new_alignment) {
                    last_vt_align = vt_align;
                    vt_align = new_alignment;
                    this.Invalidate();
                }
            );

            //  Save the "this" pointer.
            var out = this;

            //  Bind events.
            element.mousedown(function(event) {
                out.TriggerEvent("MouseDown", GetEventOffsetPosition(event));
            }).mousemove(function(event) {
                out.TriggerEvent("MouseMove", GetEventOffsetPosition(event));
            }).mouseup(function(event) {
                out.TriggerEvent("MouseUp", GetEventOffsetPosition(event));
            }).mouseleave(function(event) {
                out.TriggerEvent("MouseLeave", GetEventOffsetPosition(event));
            }).click(function(event) {
                out.TriggerEvent("Click", GetEventOffsetPosition(event));
            });

            //  Invalidate immediately after created.
            this.Invalidate();
        },
        [String],
        function(text) {
            this.constructor.call(this, text, cst_InitialFont);
        }
    );

    //  Inherit "Label" from "BaseObject".
    Inherit(Label, ns.BaseObject);

    //  Export public APIs / classes.
    Namespace.Export(ns, "Label", Label);
});